home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-07-16 | 17.6 KB | 684 lines | [TEXT/CWIE] |
- // ===========================================================================
- // CGraphDrawing.cp ©1995-97 Timo Eloranta All rights reserved.
- // ===========================================================================
- // An instance of the CGraphDrawing class represents a single possibility of
- // drawing the given graph. The population consists of these objects.
-
- #include <UException.h>
-
- #include "CGraphDrawing.h"
- #include "CCrossMemory.h"
- #include "MyUtils.h"
-
- // ---------------------------------------------------------------------------
- // • CGraphDrawing
- //
- // Called by: CPopulation::Initialize
- // ---------------------------------------------------------------------------
- // Constructor.
-
- CGraphDrawing::CGraphDrawing()
- {
- mEdgeCount = 0;
- mCrossMemo = nil;
- }
-
- // ---------------------------------------------------------------------------
- // • ~CGraphDrawing
- //
- // Called by: CPopulation::JunkGraphs
- // ---------------------------------------------------------------------------
- // Destructor.
-
- CGraphDrawing::~CGraphDrawing()
- {
- JunkCrossMemory();
- }
-
- // ---------------------------------------------------------------------------
- // • Initialize
- //
- // Called by: CPopulation::Initialize
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::Initialize(
- Int16 inNodeCount,
- Int16 inEdgeCount,
- Int16 inGridSize,
- Boolean inFirstTime)
- {
- mNodes.Initialize( inNodeCount, inGridSize, inFirstTime );
-
- if ( inEdgeCount != mEdgeCount) {
- mEdges.erase( mEdges.begin(), mEdges.end() );
- mEdges.reserve( inEdgeCount );
-
- for ( Int16 theDex = 1; theDex <= inEdgeCount; ++theDex )
- mEdges.push_back( CEdge() );
-
- mEdgeCount = inEdgeCount;
- }
-
- mMemoryIsGarbage = true;
- mEvaluationNeeded = true;
- }
-
- // ---------------------------------------------------------------------------
- // • AddCrossMemory
- //
- // Called by: CPopulation::AddCrossMemories
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::AddCrossMemory()
- {
- mCrossMemo = new CCrossMemory( mEdgeCount );
-
- ThrowIfNil_ (mCrossMemo);
- }
-
- // ---------------------------------------------------------------------------
- // • JunkCrossMemory
- //
- // Called by: CGraphDrawing::~CGraphDrawing
- // CPopulation::AddCrossMemories
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::JunkCrossMemory()
- {
- if ( mCrossMemo ) {
- delete mCrossMemo;
- mCrossMemo = nil;
- }
- }
-
- // ---------------------------------------------------------------------------
- // • ValidNewEdge
- //
- // Called by: CPopulation::SetUpEdges
- // ---------------------------------------------------------------------------
-
- Boolean
- CGraphDrawing::ValidNewEdge(
- Int16 inNodeNbr1,
- Int16 inNodeNbr2) const
- {
- CEdge theTestEdge;
-
- CNodePtr theNode1 = mNodes.GetNodePtrByNbr( inNodeNbr1);
- CNodePtr theNode2 = mNodes.GetNodePtrByNbr( inNodeNbr2);
-
- theTestEdge.Set( theNode1, theNode2);
-
- // Check if we already have this one or not...
-
- EdgeVector::const_iterator theIter = mEdges.begin();
-
- while ( theIter != mEdges.end() ) {
- if ( *theIter == theTestEdge )
- return false;
- ++theIter;
- }
- return true;
- }
-
- // ---------------------------------------------------------------------------
- // • SetNewEdge
- //
- // Called by: CPopulation::SetUpEdges
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::SetNewEdge(
- Int16 inEdgeNbr,
- Int16 inNodeNbr1,
- Int16 inNodeNbr2)
- {
- CNodePtr theNode1 = mNodes.GetNodePtrByNbr( inNodeNbr1 );
- CNodePtr theNode2 = mNodes.GetNodePtrByNbr( inNodeNbr2 );
-
- // mEdges is 0-based !! ---> - 1
-
- mEdges[ inEdgeNbr - 1 ].Set( theNode1, theNode2 );
- }
-
- // ---------------------------------------------------------------------------
- // • Mutate
- //
- // Called by: CPopulation::DoMutate
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::Mutate()
- {
- // A random selection for the mutation type
-
- Int16 theMutationType = RangedRdm( 1, 65 );
-
- if ( theMutationType <= 8 ) {
-
- switch ( theMutationType ) {
-
- case 1: mNodes.SwapRows();
- mEvaluationNeeded = true;
- break;
- case 2: mNodes.SwapCols();
- mEvaluationNeeded = true;
- break;
-
- case 3: mEvaluationNeeded =
- mNodes.SwapInRowOrCol(true) || mEvaluationNeeded;
- break;
- case 4: mEvaluationNeeded =
- mNodes.SwapInRowOrCol(false) || mEvaluationNeeded;
- break;
-
- case 5: mEvaluationNeeded =
- mNodes.InvertRow() || mEvaluationNeeded;
- break;
- case 6: mEvaluationNeeded =
- mNodes.InvertPartRow() || mEvaluationNeeded;
- break;
-
- case 7: mEvaluationNeeded =
- mNodes.InvertCol() || mEvaluationNeeded;
- break;
- case 8: mEvaluationNeeded =
- mNodes.InvertPartCol() || mEvaluationNeeded;
- break;
- }
-
- } else
- if ( theMutationType <= 18 ) {
- mNodes.SingleMutate();
- mEvaluationNeeded = true;
- }
- else
- if ( theMutationType <= 28 ) { // Edge length preserving
- EdgeMutation( true, false ); // normal edge mutation...
- mEvaluationNeeded = true;
- }
- else
- if ( theMutationType <= 40 ) { // Edge length preserving
- mEvaluationNeeded = // two (2) edge mutation...
- TwoEdgeMutation() || mEvaluationNeeded;
- }
- else
- if ( theMutationType <= 45 ) {
- mEvaluationNeeded =
- mNodes.TinyMutate() || mEvaluationNeeded;
- }
- else // Edge length preserving
- if ( theMutationType <= 50 ) { // tiny edge mutation...
- mEvaluationNeeded =
- EdgeMutation( true, true ) || mEvaluationNeeded;
- }
- else
- if ( theMutationType <= 55 ) {
- mNodes.SmallMutate();
- mEvaluationNeeded = true;
- }
- else
- if ( theMutationType <= 60 ) {
- mEvaluationNeeded =
- mNodes.LargeContMutate() || mEvaluationNeeded;
- }
- else { // Edge length changing
- EdgeMutation( false, false ); // normal edge mutation...
- mEvaluationNeeded = true;
- }
- }
-
- // ---------------------------------------------------------------------------
- // • EdgeMutation (PRIVATE)
- //
- // Called by: CGraphDrawing::Mutate
- // ---------------------------------------------------------------------------
-
- Boolean
- CGraphDrawing::EdgeMutation(
- Boolean inPreserveLength,
- Boolean inTiny )
- {
- CEdge *theEdge;
- CNodePtr theNode1, theNode2;
-
- if ( mEdgeCount == 0 ) return false;
-
- Int16 theEdgeIndex = FastRandom( mEdgeCount );
-
- theEdge = &mEdges[ theEdgeIndex ];
-
- theEdge -> GetEdgeNodes( theNode1, theNode2 );
-
- if (! inTiny ) {
-
- if ( RandomBool() )
- mNodes.MoveEdge( theNode1, theNode2, inPreserveLength );
- else
- mNodes.MoveEdge( theNode2, theNode1, inPreserveLength );
-
- return true;
- }
- else {
-
- if ( RandomBool() )
- return mNodes.TinyMoveEdge( theNode1, theNode2 );
- else
- return mNodes.TinyMoveEdge( theNode2, theNode1 );
- }
- }
-
- // ---------------------------------------------------------------------------
- // • TwoEdgeMutation (PRIVATE)
- //
- // Called by: CGraphDrawing::Mutate
- // ---------------------------------------------------------------------------
-
- Boolean
- CGraphDrawing::TwoEdgeMutation( )
- {
- CNodePtr theNode1, theNode2, theNode3;
-
- if ( mEdgeCount < 2 ) return false;
-
- if ( ThreeConnectedNodes( theNode1, theNode2, theNode3 ) ) {
- if ( RandomBool() )
- mNodes.Move3ConnectedNodes( theNode1, theNode2, theNode3 );
- else mNodes.Move3ConnectedNodes( theNode2, theNode1, theNode3 );
- } else
- mNodes.MoveEdge( theNode1, theNode2, true );
-
- return true;
- }
-
- // ---------------------------------------------------------------------------
- // • ThreeConnectedNodes (PRIVATE)
- //
- // Called by: CGraphDrawing::TwoEdgeMutation
- // CGraphDrawing::ThreeNodeCrossover
- // ---------------------------------------------------------------------------
-
- Boolean
- CGraphDrawing::ThreeConnectedNodes(
- CNodePtr &outNode1,
- CNodePtr &outNode2,
- CNodePtr &outNode3 )
- {
- CEdge *theEdge;
- Boolean theFound = false;
-
- // First we pick a random edge...
- Int16 theEdgeIndex = FastRandom( mEdgeCount );
- theEdge = &mEdges[ theEdgeIndex ];
-
- // ...which gives us already 2 of 3 needed nodes
- theEdge -> GetEdgeNodes( outNode1, outNode2 );
-
- // We still need one node which is connected to either
- // outNode1 or outNode2 by an edge. Let's see if we can find one.
-
- if ( mEdgeCount > 1 ) {
-
- EdgeVector::const_iterator
- theLast = mEdges.begin() + RangedRdm( 0, mEdgeCount - 2 );
- EdgeVector::const_iterator
- theIter = theLast + 1;
-
- while ( (! theFound ) && ( theIter != theLast ) ) {
-
- if ( ( theIter != theEdge ) &&
- ( theIter -> OtherNode( outNode1, outNode3 ) ||
- theIter -> OtherNode( outNode2, outNode3 )) )
- theFound = true;
- else {
- ++theIter;
- if ( theIter == mEdges.end() )
- theIter = mEdges.begin();
- }
- }
- }
-
- return theFound;
- }
-
- // ---------------------------------------------------------------------------
- // • ThreeNodeCrossover
- //
- // Called by: CPopulation::DoCrossover
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::ThreeNodeCrossover(
- CGraphPtr inCrosser2 )
- {
- CNodePtr theNode11, theNode12, theNode13,
- theNode21, theNode22, theNode23;
- Boolean theGotThree;
- Int16 theNbr1, theNbr2, theNbr3 = 0;
- Point thePos11, thePos12, thePos13,
- thePos21, thePos22, thePos23;
-
- if ( mEdgeCount < 1 ) return;
-
- theGotThree = ThreeConnectedNodes( theNode11, theNode12, theNode13 );
-
- theNbr1 = theNode11 -> GetNodeNbr();
- theNbr2 = theNode12 -> GetNodeNbr();
-
- theNode11 -> GetNodePos( thePos11 );
- theNode12 -> GetNodePos( thePos12 );
-
- theNode21 = inCrosser2 -> mNodes.GetNodePtrByNbr( theNbr1 );
- theNode22 = inCrosser2 -> mNodes.GetNodePtrByNbr( theNbr2 );
-
- theNode21 -> GetNodePos( thePos21 );
- theNode22 -> GetNodePos( thePos22 );
-
- if ( theGotThree ) {
- theNbr3 = theNode13 -> GetNodeNbr();
- theNode13 -> GetNodePos( thePos13 );
- theNode23 = inCrosser2 -> mNodes.GetNodePtrByNbr( theNbr3 );
- theNode23 -> GetNodePos( thePos23 );
- }
-
- mEvaluationNeeded =
- mNodes.ThreeNodeCrossover( theNbr1, theNbr2, theNbr3,
- thePos21, thePos22, thePos23 );
-
- inCrosser2 -> mEvaluationNeeded =
- mNodes.ThreeNodeCrossover( theNbr1, theNbr2, theNbr3,
- thePos11, thePos12, thePos13 );
-
- }
-
- // ---------------------------------------------------------------------------
- // • RectCrossover
- //
- // Called by: CPopulation::DoCrossover
- // CPopulation::DoRectCrossover
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::RectCrossover(
- const Rect &inSourceRect,
- const Rect &inDestRect,
- const CGraphDrawing &inBaseGraph,
- const CGraphDrawing &inRectGraph )
- {
- mNodes.RectCrossover( inSourceRect, inDestRect,
- *(inBaseGraph.GetNodes()) ,
- *(inRectGraph.GetNodes()) );
-
- mEvaluationNeeded = true;
- }
-
- // ---------------------------------------------------------------------------
- // • Evaluate (PRIVATE)
- //
- // Called by: CGraphDrawing::GetFitness
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::Evaluate( Int32 inCurLeastCrossings )
- {
- Int32 theCrossings;
-
- if ( mCrossMemo )
- theCrossings = SmarterCountSect();
- else
- theCrossings = SimpleCountSect();
-
- // We don't waste time on measuring more details, if the
- // drawing has more crossings than our current best drawing...
- // AND if we treat minimizing crossings as our primary goal.
-
- if ( CFitness::CrossingsRule && (theCrossings > inCurLeastCrossings))
- mFitness.SetMinDistance( 0L );
- else {
- CountEdgeLengths();
- mNodes.BruteForceClosestPairs( mFitness );
- }
-
- mEvaluationNeeded = false;
- }
-
- // ---------------------------------------------------------------------------
- // • DrawEdges
- //
- // Called by: CGraphPane::DrawEdges
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::DrawEdges( Int16 inOneOneXY, Int16 inSquareSize) const
- {
- EdgeVector::const_iterator theIter = mEdges.begin();
-
- while ( theIter != mEdges.end() ) {
- theIter -> Draw( inOneOneXY, inSquareSize );
- ++theIter;
- }
- }
-
- // ---------------------------------------------------------------------------
- // • SimpleCountSect (PRIVATE)
- //
- // Called by: CGraphDrawing::Evaluate
- // ---------------------------------------------------------------------------
- // The brute force way of counting the intersections in a graph.
-
- Int32
- CGraphDrawing::SimpleCountSect( )
- {
- Int32 theCount = 0L;
-
- if ( mEdgeCount > 1 ) {
-
- EdgeVector::const_iterator the1st;
- EdgeVector::const_iterator the2nd;
-
- for ( the1st = mEdges.begin();
- the1st != mEdges.end()-1; ++the1st )
- for ( the2nd = the1st + 1;
- the2nd != mEdges.end(); ++the2nd ) {
-
- if ( the1st -> Intersects( *the2nd ) )
- theCount++;
- }
- }
-
- mFitness.SetCrossings( theCount );
-
- return theCount;
- }
-
- // ---------------------------------------------------------------------------
- // • SmarterCountSect (PRIVATE)
- //
- // Called by: CGraphDrawing::Evaluate
- // ---------------------------------------------------------------------------
- // Counting the intersections using the "cross memory".
-
- Int32
- CGraphDrawing::SmarterCountSect( )
- {
- Int32 theCount = 0L;
-
- if ( mEdgeCount > 1 ) {
-
- EdgeVector::const_iterator the1st;
- EdgeVector::const_iterator the2nd;
-
- if ( mMemoryIsGarbage ) {
-
- // The following is like the brute force method used
- // by SimpleSectCount, but here we store the crossing
- // facts to our "cross memory"
-
- mCrossMemo -> ResetCrossTotal();
- mCrossMemo -> ResetScanner();
-
- for ( the1st = mEdges.begin();
- the1st != mEdges.end() - 1;
- ++the1st ) {
- for ( the2nd = the1st + 1;
- the2nd != mEdges.end();
- ++the2nd ) {
-
- mCrossMemo -> SetCrossFact(
- the1st -> Intersects( *the2nd ) );
- mCrossMemo -> AdvanceScanner();
- }
- }
-
- mMemoryIsGarbage = false;
- }
- else {
-
- // Here we use the facts stored in the "cross memory"
- // and only update the crossing facts for moved edges.
-
- Boolean theMover1;
-
- mCrossMemo -> ResetScanner();
-
- for ( the1st = mEdges.begin();
- the1st != mEdges.end() - 1;
- ++the1st ) {
- theMover1 = the1st -> HasMoved();
- for ( the2nd = the1st + 1;
- the2nd != mEdges.end();
- ++the2nd ) {
-
- if ( theMover1 || the2nd -> HasMoved() )
- mCrossMemo -> SetCrossFactByFlipping(
- the1st -> Intersects( *the2nd ) );
-
- mCrossMemo -> AdvanceScanner();
- }
- }
- }
-
- mNodes.ResetAll( ); // Set all nodes as non-movers
-
- theCount = mCrossMemo -> GetCrossTotal();
- }
-
- mFitness.SetCrossings( theCount );
-
- return theCount;
- }
-
- // ---------------------------------------------------------------------------
- // • CountEdgeLengths
- //
- // Called by: CGraphDrawing::Evaluate
- // ---------------------------------------------------------------------------
-
- void
- CGraphDrawing::CountEdgeLengths( )
- {
- Int16 theLength,
- theMin = max_Int16;
- Int32 theDeviationSum = 0;
-
- EdgeVector::iterator theIter;
-
- for ( theIter = mEdges.begin(); theIter != mEdges.end(); ++theIter ) {
- theLength = theIter -> SetLength();
- if ( theLength < theMin )
- theMin = theLength;
- }
-
- for ( theIter = mEdges.begin(); theIter != mEdges.end(); ++theIter ) {
- theLength = theIter -> GetLength();
- theDeviationSum += (theLength == theMin) ?
- theMin : theLength - (theMin + 1);
- }
-
- mFitness.SetEdgeStuff( theDeviationSum );
- }
-
- // ---------------------------------------------------------------------------
- // • BestCopy
- //
- // Called by: CPopulation::InitBestEver
- // CPopulation::Evaluate
- // ---------------------------------------------------------------------------
- // This copy routine is used for the "best ever" graph drawing
-
- void
- CGraphDrawing::BestCopy(
- const CGraphDrawing & inGD,
- Boolean inFirstTime )
- {
- mFitness = inGD.mFitness;
- mNodes = inGD.mNodes;
-
- if ( inFirstTime ) {
- mEdgeCount = inGD.mEdgeCount;
- mEvaluationNeeded = inGD.mEvaluationNeeded;
- CopyEdges( inGD );
- }
- }
-
- // ---------------------------------------------------------------------------
- // • RuntimeCopy
- //
- // Called by: CPopulation::DoSelection
- // CPopulation::DoRectCrossover
- // ---------------------------------------------------------------------------
- // Copy all the fields that change during the iterations.
- // Note that for example the edge array doesn't get copied.
-
- void
- CGraphDrawing::RuntimeCopy(
- const CGraphDrawing & inGD,
- Boolean inCopyCrossMemory )
- {
- mFitness = inGD.mFitness;
- mEvaluationNeeded = inGD.mEvaluationNeeded;
- mMemoryIsGarbage = inGD.mMemoryIsGarbage;
- mSelectionOriginal = inGD.mSelectionOriginal;
-
- mNodes.mNodesArray = inGD.mNodes.mNodesArray;
-
- if ( mCrossMemo ) {
- if ( inCopyCrossMemory )
- *mCrossMemo = *(inGD.mCrossMemo);
- else
- mMemoryIsGarbage = true;
- }
- }
-
- // ---------------------------------------------------------------------------
- // • CopyEdges (PRIVATE)
- //
- // Called by: CGraphDrawing::BestCopy
- // ---------------------------------------------------------------------------
- // Because of the pointers to nodes we can not simply do this by writing
- // mEdges = inGD.mEdges; (Trust me, I tried... :-)
-
- void
- CGraphDrawing::CopyEdges( const CGraphDrawing & inGD)
- {
- Int16 theEdgeDex, theNbr1, theNbr2, theCopySize;
-
- if ( mEdges.size() != (theCopySize = inGD.mEdges.size())) {
- mEdges.erase ( mEdges.begin(), mEdges.end() );
- mEdges.reserve( theCopySize );
-
- for ( theEdgeDex = 1; theEdgeDex <= mEdgeCount; ++theEdgeDex )
- mEdges.push_back( CEdge() );
- }
-
- for ( theEdgeDex = 0; theEdgeDex <= mEdgeCount-1; ++theEdgeDex ) {
-
- inGD.mEdges[ theEdgeDex ].GetNodeNumbers( theNbr1, theNbr2 );
- mEdges[ theEdgeDex ].Set( mNodes.GetNodePtrByNbr( theNbr1 ),
- mNodes.GetNodePtrByNbr( theNbr2 ) );
- }
- }
-